package martin.tpfinal.grabar.grafico;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import javax.swing.Timer;

import martin.tpfinal.grabar.motor.ColaSalida;

/** 
 * Reads data from the input channel and writes to the output stream
 */
class Capturar implements Runnable {

    private TargetDataLine line;
    private Thread thread;
    private VentanaPrincipal ventana;
	
	
    public Capturar(VentanaPrincipal ventana){
    	this.ventana=ventana;
    }
    
    
    public void start() {
        ventana.setErrStr(null);
        thread = new Thread(this);
        thread.setName("Capture");
        thread.start();
    }

    public void stop() {
        thread = null;
    }
    
    public void shutDown(String message) {
    	ventana.setErrStr(message);
    	if (ventana.getErrStr() != null && thread != null) {
            thread = null;
            ventana.getSamplingGraph().stop();
            ventana.getPausB().setEnabled(false);
            ventana.getCaptB().setText("Grabar");
            System.err.println(ventana.getErrStr());
            ventana.getSamplingGraph().repaint();
        }
    }

    public void run() {

    	ventana.setDuration(0);
        numProceso=0;
        ventana.setAudioInputStream(null);
                
        AudioFormat format = ventana.getFormatControls().getFormat();
        
        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
              
        System.out.println(format.toString());
        
        if (!AudioSystem.isLineSupported(info)) {
            shutDown("Line matching " + info + " not supported.");
            return;
        }

        // get and open the target data line for capture.
        try {
            line = (TargetDataLine) AudioSystem.getLine(info);
            line.open(format, line.getBufferSize());
        } catch (Exception ex) { 
            shutDown(ex.toString());
            return;
        }

        // play back the captured audio data
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int frameSizeInBytes = format.getFrameSize();
        int numBytesRead;
        
        //Si uso 11025 HZ, con 8bits (1 byte), entonces frameRate = 11025 y framezise es 1, si los multiplico me da el numero de bytes que leera en 1 seg
        //entonces elijo la cantidad que voy a ir capturando
        
        /*                       bytes               bytes 
         * 11025 / 63 = 175    ( 11025 -> 1 seg  ==>  175 -> 0.0158 seg)
         * 11025 / 45 = 245    ( 11025 -> 1 seg  ==>  245 -> 0.022 seg)
         * 11025 / 35 = 315    ( 11025 -> 1 seg  ==>  315 -> 0.028 seg)
         * 11025 / 75 = 147    ( 11025 -> 1 seg  ==>  147 -> 0.013 seg)
         *
         *                     ( 6615 -> 0.6 seg   - usando el de 245 tengo que tomar 27 muestras   (0.6 / 0.022) o (27 * 245)
         *                                         - usando el de 315 tengo que tomar 21 muestras   
         *                                         - usando el de 175 tengo que tomar 38 muestras
         *                                         - usando el de 147 tengo que tomar 45 muestras
         *
         * 0.6 es el tiempo que grabo para capturar las palabras 
         *                                         
         *                     ( 7718 -> 0.7 seg   - usando el de 147 tengo que tomar 52 muestras
         *
         * 0.7 es el tiempo que grabo para capturar las palabras                                        
         * */
        
        int divParaObtenerdiezmil=75;
        int muestras = 52;
        
        int numBytesCadaDiezMili = (int)((format.getFrameRate()*format.getFrameSize())/divParaObtenerdiezmil);
        byte[] data2 = new byte[numBytesCadaDiezMili];
        
        Timer timer=createTmmer();
        line.start();
                                    
//        timer.start();
        
        int counter=0;
        boolean esperando=true;
        
        //voy a suponer que mis palabras deben durar 0.65 segundos, serian 650 milisegundos
        //entonces cuando detecto que es una palabra, debo grabar 65 veces del read
        while (getThread() != null) {

            if((numBytesRead = line.read(data2, 0, numBytesCadaDiezMili)) == -1) {
                break;
            }
            
            /*
             * Grabando en calidad estudio
             * Volumen mic 70
             * potencia +30
             */
            
            processData(numBytesRead,data2);
            
            if (esperando){
//              System.out.println("Esta esperando***************");
                int e=getEnergy(numBytesRead, data2);
                int ZCR=getZeroCrosingRateWithoutCero(numBytesRead, data2);
                if (e > 100 && ZCR > 5 ){  // estaba en 100 y 10
                    out = new ByteArrayOutputStream();
                    out.write(data2, 0, numBytesRead);
                    counter++;
                    esperando=false;
                }
            }
            else{
                out.write(data2, 0, numBytesRead);
                counter++;
            }
            
            if (counter == muestras){
                procesarYenviarpalabra(out,format,frameSizeInBytes);
                counter=0;
                esperando=true;
                System.out.println("Grabo una***************");
            }
        }
        timer.stop();

        // we reached the end of the stream.  stop and close the line.
        line.stop();
        line.close();
        line = null;

        System.out.println(("Totales potencia: "+mayorPot));
        System.out.println(("Totales cero: "+mayorCero));
        
        // stop and close the output stream
        try {
            out.flush();
            out.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }


	}

    private void procesarYenviarpalabra(ByteArrayOutputStream out,AudioFormat format,int frameSizeInBytes) {
        // stop and close the output stream
        try {
            out.flush();
            out.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        // load bytes into the audio input stream for playback
        byte audioBytes[] = out.toByteArray();
        ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
        AudioInputStream audioFile=new AudioInputStream(bais, format, audioBytes.length / frameSizeInBytes);
        ventana.setAudioInputStream( audioFile);

        long milliseconds = (long)((ventana.getAudioInputStream().getFrameLength() * 1000) / format.getFrameRate());
        ventana.setDuration( milliseconds / 1000.0);

        try {
            ventana.getAudioInputStream().reset();
        } catch (Exception ex) { 
            ex.printStackTrace(); 
            return;
        }

//        ventana.getSamplingGraph().createWaveForm(audioBytes);

//        String fileName="C:\\Users\\Martin\\Desktop\\sonidos\\martin\\variosTonos\\otros\\otros"+index++ +".wav";
//        ventana.saveToFile(fileName, AudioFileFormat.Type.WAVE);
//        System.out.println(fileName);
        
        enviarpalabra(audioBytes);


	}
    
    
    int index=1;
    
    int numProceso=0;
    private void processData(int numBytesRead, byte[] data) {
        int max=0;
        int min=0;
        int actual=0;
//        System.out.println("-----------------------"+ numProceso++ +"-----------------------------");
//        System.out.println("leyo: "+numBytesRead);
        for (int i = 0; i < numBytesRead; i++) {
            actual=data[i];
//          System.out.print(actual+" ; ");
            if (max<actual)
                max=actual;

            if (min>actual)
                min=actual;

        }
//      System.out.println();
//      System.out.println("El maximo es : "+max+"; El minimo es :"+min);
//      int e = getEnergy(numBytesRead, data);
//      if (e>15000){ System.out.println("********************* MAYOR A 15000"); mayorPot++;}
//      Integer CERO = getZeroCrosingRateWithoutCero(numBytesRead, data);
//      if (CERO>29){ System.out.println("********************** MAYOR A 29"); mayorCero++;}

    }

    static int mayorPot=0;
    static int mayorCero=0;
    
    private int getEnergy(int numBytesRead, byte[] data) {
        int sum=0;
        for (int i = 0; i < numBytesRead; i++) {
            sum+=Math.abs(data[i]);
        }
//      System.out.println("La enegia es: "+sum);
        return sum;
    }



    private int getZeroCrosingRateWithoutCero(int numBytesRead, byte[] data) {
        int sum=0;

        int prevValue=data[0]; 
        
        for (int i = 1; i < numBytesRead-1; i++) {
            
            if (data[i]!=0){
                if (data[i]>0 && prevValue<0 && data[i+1]>0) 
                    sum++;
                if (data[i]<0 && prevValue>0 && data[i+1]<0) 
                    sum++;
                prevValue=data[i];
            }
        }
//      System.out.println("Cruza el 0 : "+sum);
        return sum;
    }

    private int getZeroCrosingRateWithoutCeroOld(int numBytesRead, byte[] data) {
        int sum=0;

        int prevValue=data[0]; 
        
        for (int i = 1; i < numBytesRead; i++) {
            
            if (data[i]!=0){
                if (data[i]>0 && prevValue<0) 
                    sum++;
                if (data[i]<0 && prevValue>0) 
                    sum++;
                prevValue=data[i];
            }
        }
        System.out.println("Cruza el 0 : "+sum);
        return sum;
    }

    /*
     * Se calcula sobre 100 mseg, en nuestro caso 10 periodos, ya que 1 periodo -> 10 mseg
     * 
     */
    private double getEnergyPaper(int[] data) {
        //valor umbral de energia

        //el pico de energia maximo del silencio
        double IMX=0;
        //la energia promedio del silencio
        double IMN=0;
            
        //el limite inferior del umbral
        double ITL=0;
        //el limite superior del umbral
        double ITU=0;
        
        IMX=data[0];
        for (int i = 1; i < data.length; i++) {
            if (data[i]>IMX)
                IMX=data[i];
        }
        IMN=media(data);
        double desv=desviacionEstandar(data);
        System.out.println("Maxima: "+IMX+", media: "+IMN+", desviacion: "+desv);
        
        double I1=0.03*(IMX-IMN)+IMN;
        double I2=4*IMN;
        ITL=Math.min(I1,I2);
        ITU=5*ITL;
        System.out.println("ENER      ITL : "+ITL+", ITU: "+ITU);
        
        //probar con 1.5*IMX o la media mas 2 veces la desviacion (estuve usando 10000)
        double enegia=Math.max(1.5*IMX,IMN+2*desv);//,2000);
        
        System.out.println("ENER            1.5*IMX: "+1.5*IMX+ "|||| IMN+2*desv: "+(IMN+2*desv));
        return enegia;  
        
    }
    
    
    /*
     * Se calcula sobre 100 mseg, en nuestro caso 10 periodos, ya que 1 periodo -> 10 mseg
     * 
     */
    private double getZeroCrosingRatePaper(int[] data) {
        //valor umbral de cruces de cero
        //es el minimo entre un valor umbral fijo IF y (la suma del valor medio + la desviacion estandar)
        // IZCT = MIN ( IF , IZC + 2G(IZC) )
        double IZCT=0;
            
        //umbral fijo   //125
        double IF=  80;
        
        //media de la taza de cruces de cero durante el silencio 
        //(sumo todos los calculos que hice en los distintos periodos y los dividos por la cantidad que son, es un promedio comun)
        double media=media(data);
        double desvStandar=desviacionEstandar(data);
        System.out.println("CERO Media: "+media+", desviacion: "+desvStandar);
        double IZC_final = media(data)+(2*desviacionEstandar(data));
        System.out.println("CERO IZC : "+IZC_final);
        IZCT=Math.max(IF, IZC_final);
        //
        return IZCT;    
        
    }
    
    
    public double media(int val[]){
        int suma=0;
        for (int i = 0; i < val.length; i++) {
            suma+= val[i];
        }
        
        return suma/val.length;
    }
    
    public double desviacionEstandar(int arreglo[])  {
        double suma = 0;
        double media = media(arreglo);
        double desviacion = 0;
        double diferencia = 0;
        
        for(int i = 0; i < arreglo.length; i++) {
            diferencia = (arreglo[i] - media);
            desviacion += Math.pow(diferencia, 2);
        }
        desviacion = desviacion/arreglo.length;
        return Math.sqrt(desviacion);
        
    }

    
    int tiempo=1;
    private Timer createTmmer() {
        tiempo=0;
        Timer timer = new Timer (1000, new ActionListener ()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println();
                System.out.println("***********Paso el segundo: "+tiempo++);
                System.out.println();
             }
        });
                        
        return timer;
    }

	public TargetDataLine getLine() {
		return line;
	}


	public void setLine(TargetDataLine line) {
		this.line = line;
	}


	public Thread getThread() {
		return thread;
	}


	public void setThread(Thread thread) {
		this.thread = thread;
	}

    /**
     * Cuando reconozco que no es silencio, entonces grabo cierto tiempo, y llamo a esta funcion
     * esta se encargara de enviar la informacion por socket al reconocedor de palabras
     * de esta forma el sistema sigue grabando sonido, y puedo reconocer continuamente.
     */
    private void enviarpalabra(byte[] audioFile) {
        ColaSalida cola=ColaSalida.getInstance();
        cola.addElement(audioFile);
        
        System.out.println("encolo una");
        
    }


	
	
	

} // End class Capture
